import { init, withCancelToken, makeRequest, RequestArgs } from './utils';
import Axios from 'axios';
import { AxiosVue3Config } from './axiosVue3Plugin';

/**
 * 
 * @param options you can also use method chaining to do configuration.
 * 
 * e.g., const leadingAxios = leadingAxios().axiosInstance(xxx).autoRetry(xxx).cancelWhenUnmounted(xxx)
 * 
 * @returns a wrapped axios instance with leading mode.
 * 
 * It provide exactly same api with axios to make request.
 * 
 * It make request at leading mode, that is when one request is in process, all other incoming request will be skipped.
 * 
 * It also provide reactive `loading`, `data` and `error` properties.
 * 
 * It provide `cancel` method to cancel current request.
 * 
 */
export const leadingAxios = <T>(options: AxiosVue3Config = {}) => {

  const { config, state, axios, axiosReadonly, handler } = init<T>(options);

  handler.run = <R>(args: RequestArgs): Promise<R> => {
    if (state.loading) {
      // Forever pending Promise, should no memory leak on modern browsers.
      return new Promise<R>(() => { });
    }
    state.loading = true;
    axios.loading = true;
    axios.error = undefined;
    state.source = Axios.CancelToken.source();
    const argsWithCancelToken = withCancelToken(args, state.source.token);
    const instance = config.axiosInstance;
    return new Promise<R>((resolve, reject) => {
      let triedCount = 0;
      const onSuccess = (res: R): void => {
        state.loading = false;
        axios.loading = false;
        if (res) {
          axios.data = (res as any).data;
        }
        resolve(res);
      }
      const onError = (err: any): void => {
        if (Axios.isCancel(err)) {
          state.loading = false;
          axios.loading = false;
        } else {
          if (config.autoRetry && config.autoRetry.maxCount > triedCount) {
            triedCount += 1;
            if (config.autoRetry.interval > 0) {
              state.timer = setTimeout(() => {
                makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError);
              }, config.autoRetry.interval)
            } else {
              makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError);
            }
          } else {
            state.loading = false;
            axios.loading = false;
            axios.error = err;
            reject(err);
          }
        }
      }
      makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError)
    })
  }

  return axiosReadonly;
}